/*
*   g++ -o OGR_drv.so -fpic -shared -I/usr/local/lib/erlang/usr/include/ OGR_drv.cpp -lgdal
* gcc -o OGR_drv.so -I/usr/local/lib/erlang/usr/include/ -fpic -shared OGR_drv.cpp -lpthread -lgdal
*
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>

#include "ai_image.h"
#include "erl_driver.h"

#ifdef MAC_OS_X
	#include "GDAL/ogrsf_frmts.h"
	#include <GDAL/ogr_spatialref.h>
#else
	#include "ogrsf_frmts.h"
	#include <ogr_spatialref.h>
#endif

#define NAME_SIZE 256

#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
                      (((unsigned char*) (s))[1] << 16) | \
                      (((unsigned char*) (s))[2] << 8)  | \
                      (((unsigned char*) (s))[3]))

#define put_int32(i, s) {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \
                        ((char*)(s))[1] = (char)((i) >> 16) & 0xff; \
                        ((char*)(s))[2] = (char)((i) >> 8)  & 0xff; \
                        ((char*)(s))[3] = (char)((i)        & 0xff);}

/* op codes */
#define XX_OPEN             'o'
#define XX_OPEN_WITH    'm'

/* return codes */
#define XX_VALUE        'v'
#define XX_OK           'o'
#define XX_FERROR       'E'

static ErlDrvData OGR_start(ErlDrvPort port, char *buf);
static void OGR_stop(ErlDrvData drv_data);

static ErlDrvEntry OGR_driver_entry;

typedef struct _desc {
    ErlDrvPort port;
    FILE *log;
    char cell_id[NAME_SIZE];
    OGRDataSource *dataset;
} Desc;

static ErlDrvData OGR_start(ErlDrvPort port, char *buf) {
    Desc *d = (Desc*) driver_alloc(sizeof (Desc));
    if (d == NULL)
        return (ErlDrvData) -1;

    d->dataset = NULL;
    d->log = NULL;

d->log = fopen("./OGR_drv.log", "a");
if (d->log == NULL) {
    fprintf(stderr, "log NOT open!\n");
}
else {
    fprintf(d->log, "OGR_start ing: dataset = %ld\n", d->dataset);
    fflush(d->log);
}

    memset(d->cell_id, 0, NAME_SIZE);
    d->port = port;

    set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);

    return (ErlDrvData) d;
}

static void OGR_stop(ErlDrvData drv_data) {
    Desc *d = (Desc*) drv_data;
    if (d->dataset != NULL) {
        OGRDataSource::DestroyDataSource(d->dataset);
        d->dataset = NULL;
    }
    if (d->log != NULL) {
fprintf(d->log, "OGR_drv stop, for cell: %s\n", d->cell_id);
        fclose(d->log);
        d->log = NULL;
    }
    driver_free(d);
}


static int OGR_init(void) {
FILE *fp = fopen("./OGR_drv.log", "a");
    OGRRegisterAll();
fprintf(fp, "OGR_init ed\n");
fclose(fp);
    return 0;
}

static void OGR_finish()
{
    /*
     * Make sure any remaining buffers are flushed (this is done on exit() by
     * the normal stdio).
     */
FILE *fp = fopen("./OGR_drv.log", "a");
fprintf(fp, "OGR_finish ing\n");
fclose(fp);
}

static char *driver_error(ErlDrvPort port, int err) {
    char response[256];     /* Response buffer. */
    char* s;
    char* t;
    ErlDrvBinary* bin;

    bin = driver_alloc_binary(1 + strlen (erl_errno_id (err)));
    bin->orig_bytes[0] = XX_FERROR;

    response[0] = XX_FERROR;
    for (s = erl_errno_id(err), t = bin->orig_bytes + 1; *s; s++, t++)
    *t = tolower(*s);
    return (char *)bin;
}

static char *driver_ok(ErlDrvPort port) {
    char ch = XX_OK;
    ErlDrvBinary* bin;
    bin = driver_alloc_binary(1);
    bin->orig_bytes[0] = ch;
    return (char *)bin;
}

/*
* The Core of OGR Port Driver
*
*/
static int OGR_control(ErlDrvData drv_data,
            unsigned int command,
            char *buf, int len,
            char **rbuf, int rlen) 
{
    Desc *desc = (Desc*) drv_data;
    ErlDrvBinary* bin;
    switch (command) {
        case XX_OPEN_WITH: {
            if (desc->dataset != NULL) {
fprintf(desc->log, "ERROR open with cell(id: %s)\t", desc->cell_id);
fflush(desc->log);
                *rbuf = driver_error(desc->port, EINVAL);
                return 1;
            }
            int filename_len = (int)buf[0];
            memset(desc->cell_id, 0, NAME_SIZE);
            memcpy(desc->cell_id, buf+1, filename_len);
fprintf(desc->log, "%s\n", desc->cell_id);

            char *pszDriverName = "Memory";
            OGRSFDriver *poDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(pszDriverName);

            if( poDriver == NULL ) {
fprintf(desc->log, "%s driver not available.\n", pszDriverName );
                *rbuf = driver_error(desc->port, EINVAL);
                return 1;
            }
            else {
fprintf(desc->log, "driver name: %s\n", poDriver->GetName());
fflush(desc->log);
            }
            
            OGRDataSource *poDS = NULL;
            poDS = OGRSFDriverRegistrar::Open( "xgl1.shp", FALSE );
            if( poDS == NULL ) {
fprintf(desc->log, "Open failed.\n" );
                *rbuf = driver_error(desc->port, EINVAL);
                return 1;
            }
fprintf(desc->log, "layer count: %d\n", poDS->GetLayerCount());
fflush(desc->log);
            
            OGRSFDriver *jsonDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName("GeoJSON");
            OGRDataSource
                    
            *rbuf = driver_ok(desc->port);
            return 1;

        }
    }
    return 1;
}

/*
 * Initialize and return a driver entry struct
 */

extern "C" DRIVER_INIT(OGR_drv)
{
    OGR_driver_entry.init         = OGR_init;   /* Not used */
    OGR_driver_entry.start        = OGR_start;
    OGR_driver_entry.stop         = OGR_stop;
    OGR_driver_entry.output       = NULL;
    OGR_driver_entry.ready_input  = NULL;
    OGR_driver_entry.ready_output = NULL;
    OGR_driver_entry.driver_name  = "OGR_drv";
    OGR_driver_entry.finish       = OGR_finish;
    OGR_driver_entry.outputv      = NULL;
    OGR_driver_entry.control      = OGR_control;
    return &OGR_driver_entry;
}

